﻿////////////////////////////////////////////////////////////////
///
/// File: Interop.cs
/// Author: Cristian Dittamo
/// Last update: 25 March 2010
/// Description: This file contains the definition of the 
///              main classes needed for invoking nVidia driver functions.
/// To do: 
/// ------------------------------------------------------------
/// Copyright (c) 2010 Cristian Dittamo (dittamo@di.unipi.it)
/// 
/// The use and distribution terms for this software are 
/// contained in the file named license.txt, which can be found 
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to 
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------
////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;

/// TODO:
namespace CompileLib
{
    namespace InteropServices
    {
        #region Wrappers on GPU Types

        [StructLayout(LayoutKind.Sequential)]
        public struct GPUContext
        {
            public IntPtr Pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GPUDeviceptr
        {
            public int Pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GPUDevice
        {
            public GPUDevice(int value)
            {
                Pointer = value;
            }

            public int Pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GPUFunction
        {
            public IntPtr Pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GPUModule
        {
            public IntPtr Pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GPUStream
        {
            public IntPtr Pointer;
        }

        #endregion

        //public class DriverCalc
        //{
        //    protected const int STRMDIM = 5;
        //    protected int _inIndex;
        //    protected GPUDeviceptr[] _input;
        //    protected int _outIndex;
        //    protected GPUDeviceptr[] _output;

        //    public GPUFunction func;

        //    public DriverCalc()
        //    {
        //        func.Pointer = IntPtr.Zero;
        //        _inIndex = -1;
        //        _input = new GPUDeviceptr[STRMDIM];
        //        _output = new GPUDeviceptr[STRMDIM];
        //        _outIndex = -1;
        //    }

        //    public DriverCalc(int InStreamDim, int OutStreamDim)
        //    {
        //        func.Pointer = IntPtr.Zero;
        //        _inIndex = -1;
        //        _input = new GPUDeviceptr[InStreamDim];
        //        _output = new GPUDeviceptr[OutStreamDim];
        //        _outIndex = -1;
        //    }

        //    ~DriverCalc()
        //    {
        //        Array.Clear(_input, 0, _input.Length);
        //        Array.Clear(_output, 0, _output.Length);
        //    }

        //    public int AddInput(GPUDeviceptr resource)
        //    {
        //        _input[++_inIndex] = resource;
        //        return _inIndex;
        //    }

        //    public int AddOutput(GPUDeviceptr resource)
        //    {
        //        _output[++_outIndex] = resource;
        //        return _outIndex;
        //    }

        //    public GPUDeviceptr GetInput(int index)
        //    {
        //        if ((index >= 0) && (index < _input.Length))
        //            return _input[index];
        //        else
        //            throw new ArgumentOutOfRangeException();
        //    }

        //    public GPUDeviceptr GetOutput(int index)
        //    {
        //        if ((index >= 0) && (index < _output.Length))
        //            return _output[index];
        //        else
        //            throw new ArgumentOutOfRangeException();
        //    }
        //}

        //public class DriverComp
        //{
        //    protected GPUDevice _deviceID;
        //    protected DriverCalc[] _funcs;
        //    protected int _nrComp;
        //    protected int _indexFuncs;

        //    public GPUContext context;
        //    public GPUModule module;

        //    public DriverComp(int nrKernels)
        //    {
        //        _deviceID.Pointer = 0;
        //        context.Pointer = IntPtr.Zero;
        //        module.Pointer = IntPtr.Zero;
        //        _indexFuncs = -1;
        //        _nrComp = nrKernels;
        //        _funcs = new DriverCalc[_nrComp];
        //    }

        //    ~DriverComp()
        //    {
        //        context.Pointer = IntPtr.Zero;
        //    }

        //    public GPUDevice Device
        //    {
        //        get { return _deviceID; }
        //        set { _deviceID = value; }
        //    }

        //    public int AddFunction(DriverCalc calc)
        //    {
        //        _funcs[++_indexFuncs] = calc;
        //        return _indexFuncs;
        //    }

        //    public DriverCalc GetFunction(int index)
        //    {
        //        if ((index >= 0) && (index < _funcs.Length))
        //            return _funcs[index];
        //        else
        //            throw new ArgumentOutOfRangeException();
        //    }
        //}
        
        namespace NVIDIA
        {
            //public class CUDADriverCalc : DriverCalc
            //{
            //    public CUDADriverCalc() : base() { }
            //    public CUDADriverCalc(int InStreamDim, int OutStreamDim) : base(InStreamDim, OutStreamDim)
            //    {}
            //    ~CUDADriverCalc()
            //    {
            //        for (int i = 0; i < _input.Length; i++)
            //            if (_input[i].Pointer != 0)
            //                CUDA.MFree(_input[i]);
            //        for (int i = 0; i < _output.Length; i++)
            //            if (_output[i].Pointer != 0)
            //                CUDA.MFree(_output[i]);
            //    }
            //}

            //public class CUDADriverComp : DriverComp
            //{
            //    public CUDADriverComp(int nrKernels) : base(nrKernels) { }

            //    ~CUDADriverComp()
            //    {
            //        IntPtr tmp = module.Pointer;
            //        NVIDIA.CUDA.UnLoadModule(tmp);
            //        module.Pointer = IntPtr.Zero;
            //    }
            //}

            public sealed class CUDA
            {
                #region Stefano Dll Imports

                public enum memcpyKind { DEVICE_TO_HOST, HOST_TO_DEVICE, DEVICE_TO_DEVICE };
                public enum memoryMode { PINNED, PAGEABLE };

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern float bandwidthTest(memcpyKind kind, memoryMode mode, bool wc);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern float latencyTest(memcpyKind kind, memoryMode mode, bool wc);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void StartTimer();

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void StopTimer();

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern float GetElapsedTime();

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int GetWarpSize(int deviceID);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int GetSMCount(int deviceID);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int GetClockRate(int deviceID);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern string GetDeviceName(int deviceID);

                #endregion

                #region Device properties

                ////[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                ////public static extern int GetDevPrpNAME(int devID, [MarshalAs(UnmanagedType.LPStr)] ref String name);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpTOTGLOBMEM(int devID, ref IntPtr totalGlobalMem);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpSHAREDXBLK(int devID, ref IntPtr sharedMemPerBlock);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpREGSXBLK(int devID, ref int regsPerBlock);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpWARP(int devID, ref int warpSize);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpPITCH(int devID, ref IntPtr memPitch);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpTHRDXBLK(int devID, ref int maxThreadsPerBlock);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpMAXTHRD(int devID, int[] maxThreadsDim);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpMAXGRID(int devID, int[] maxGridSize);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpCONSTMEM(int devID, ref IntPtr totalConstMem);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpVrCLK(int devID, ref int clockRate);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int GetDevPrpCANOVERLAP(int devId, ref int canoverlap);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int GetDevPrpVersion([In] GPUDevice devID, [Out] IntPtr major, [Out] IntPtr minor);
                
                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int GetDevPrpPROCNUM([In, Out] GPUDevice devID, [In, Out] ref int multiProcessorCount);

                #endregion

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void GetDevices(ref int nrDevices);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern IntPtr InitEnv([In] GPUDevice device, [In, Out] int numArg, [In, Out] int numFuncs);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void Exit([In,Out] IntPtr compute);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern GPUContext createContext([In] IntPtr compute);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void deleteContext([Out] IntPtr compute);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern GPUModule LoadBin([In] IntPtr compute, [MarshalAs(UnmanagedType.LPStr)] String ptxFile);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void UnLoadModule([Out] IntPtr compute);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern GPUFunction LoadFunc(IntPtr compute, [MarshalAs(UnmanagedType.LPStr)] String kernel_name);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int MAllocOnGPU([In, Out] IntPtr compute, int mem_size);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern void MFree([In, Out] GPUDeviceptr mem);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void MCpyCPU2GPU([In, Out] IntPtr compute, int devIndex, [In, Out] IntPtr host, int mem_size);

                //CUdeviceptr device, void* destHost, int mem_size
                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void MCpyGPU2CPU([In, Out] IntPtr compute, [In, Out] IntPtr host, int devIndex, int mem_size);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int SetBlockShape([In] GPUFunction function, int x, int y, int z);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void SetSharedSize([In, Out] GPUFunction function, int mem_size);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern void SetStreamF(int compID, int funcID, int in_param, int offset, int resID);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern int SetParamF(int compID, int funcID, int in_param, int offset, int mem_size);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int SetParamI([In, Out] IntPtr compute, [In, Out] GPUFunction function, int offset, int value);

                //[DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                //public static extern void SetParamPtr([In, Out] GPUFunction function, int offset, [In, Out] GPUDeviceptr value);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern int SetParamV(IntPtr compute, GPUFunction function, int offset, int devIndex);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void SetParamSize([In, Out] GPUFunction function, int mem_size);

                [DllImport("NVIDIALib.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                public static extern void LaunchGrid([In, Out] GPUFunction function, int gridWidth, int gridHeight);

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern int CrtTimer();

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern void DelTimer(int timerId);

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern void StrtTimer(int timerId);

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern void StpTimer(int timerId);

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern void ElpstTime(int timerId);

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern void StrmCreate(int num);

                ////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                ////public static extern void StrmDelete(int num);

                //////[DllImport("NVIDIALib.dll", CharSet = CharSet.Auto)]
                //////static extern void StrmQuery();

                private CUDA()
                {

                }
            }
        }

        namespace AMD
        {
            sealed class CAL
            {
                //public static void Init() 
                //{
                //}
                //[DllImport("CALLib.dll", SetLastError = true)]
                //static extern void InitEnv();

                private CAL() { }
            }
        }
    }
}
